package com.guaji.system.chat.mqtt

import android.content.Context
import androidx.lifecycle.LiveData
import androidx.lifecycle.MutableLiveData
import kotlinx.coroutines.*
import org.eclipse.paho.android.service.MqttAndroidClient
import org.eclipse.paho.client.mqttv3.*

class AndroidMqttClient(
    context: Context,
    serverUri: String,
    clientId: String,
    username1: String? = null,
    password1: String? = null
) {

    private val u = username1
    private val p = password1
    private val client: MqttAndroidClient = MqttAndroidClient(
        context,
        serverUri,
        clientId,
    )

    private val coroutineScope = CoroutineScope(Dispatchers.IO)
    private val _connectionState = MutableLiveData<ConnectionState>()
    val connectionState: LiveData<ConnectionState> = _connectionState

    private val _receivedMessages = MutableLiveData<Pair<String, String>>()
    val receivedMessages: LiveData<Pair<String, String>> = _receivedMessages

    sealed class ConnectionState {
        object Connecting : ConnectionState()
        object Connected : ConnectionState()
        object Disconnected : ConnectionState()
        data class Error(val message: String) : ConnectionState()
    }

    init {
        setupCallback()
    }

    private fun setupCallback() {
        client.setCallback(object : MqttCallbackExtended {
            override fun connectComplete(reconnect: Boolean, serverURI: String?) {
                coroutineScope.launch(Dispatchers.Main) {
                    _connectionState.value = ConnectionState.Connected
                }
            }

            override fun connectionLost(cause: Throwable?) {
                coroutineScope.launch(Dispatchers.Main) {
                    _connectionState.value = ConnectionState.Error(
                        cause?.message ?: "Connection lost"
                    )
                }
            }

            override fun messageArrived(topic: String?, message: MqttMessage?) {
                message?.let {
                    coroutineScope.launch(Dispatchers.Main) {
                        _receivedMessages.value = Pair(topic ?: "number_topic_gears", String(it.payload))
                    }
                }
            }

            override fun deliveryComplete(token: IMqttDeliveryToken?) {
                // 消息发送完成回调
            }
        })
    }

    fun connect(
        keepAliveInterval: Int = 60,
        connectionTimeout: Int = 30,
        cleanSession: Boolean = true,
        autoReconnect: Boolean = true
    ) {
        if (client.isConnected) return

        coroutineScope.launch {
            try {
                _connectionState.postValue(ConnectionState.Connecting)

                val options = MqttConnectOptions().apply {
                    this.keepAliveInterval = keepAliveInterval
                    this.connectionTimeout = connectionTimeout
                    this.isCleanSession = cleanSession
                    this.isAutomaticReconnect = autoReconnect

                    userName = u
                    password = p?.toCharArray()
                }

                client.connect(options, null, object : IMqttActionListener {
                    override fun onSuccess(asyncActionToken: IMqttToken?) {
                        coroutineScope.launch(Dispatchers.Main) {
                            _connectionState.value = ConnectionState.Connected
                        }
                    }

                    override fun onFailure(asyncActionToken: IMqttToken?, exception: Throwable?) {
                        coroutineScope.launch(Dispatchers.Main) {
                            _connectionState.value = ConnectionState.Error(
                                exception?.message ?: "Connection failed"
                            )
                        }
                    }
                })
            } catch (e: Exception) {
                _connectionState.postValue(ConnectionState.Error(e.message ?: "Error"))
            }
        }
    }

    fun disconnect() {
        coroutineScope.launch {
            try {
                if (client.isConnected) {
                    client.disconnect()
                }
                _connectionState.postValue(ConnectionState.Disconnected)
            } catch (e: Exception) {
                _connectionState.postValue(ConnectionState.Error(e.message ?: "Error"))
            }
        }
    }

    fun subscribe(topic: String, qos: Int = 1) {
        coroutineScope.launch {
            try {
                if (!client.isConnected) {
                    connect()
                }

                client.subscribe(topic, qos)
            } catch (e: Exception) {
                _connectionState.postValue(ConnectionState.Error(e.message ?: "Error"))
            }
        }
    }

    fun unsubscribe(topic: String) {
        coroutineScope.launch {
            try {
                if (client.isConnected) {
                    client.unsubscribe(topic)
                }
            } catch (e: Exception) {
                _connectionState.postValue(ConnectionState.Error(e.message ?: "Error"))
            }
        }
    }

    fun publish(topic: String, payload: String, qos: Int = 1, retained: Boolean = false) {
        coroutineScope.launch {
            try {
                if (!client.isConnected) {
                    connect()
                }

                val message = MqttMessage(payload.toByteArray()).apply {
                    this.qos = qos
                    this.isRetained = retained
                }

                client.publish(topic, message)
            } catch (e: Exception) {
                _connectionState.postValue(ConnectionState.Error(e.message ?: "Error"))
            }
        }
    }

    fun destroy() {
        coroutineScope.cancel()
        disconnect()
    }
}